from django.contrib.gis import forms
from models import ConnieRun, LarvalPhase, LarvalPhaseLevel, VelocityFieldsDepth
from django.forms.formsets import DELETION_FIELD_NAME
from django.forms.models import BaseInlineFormSet, inlineformset_factory

import zlib

class ConnieRunForm(forms.ModelForm):
    sourceChoices = ((1, 'source'),
                     (0, 'sink'))
    isSource = forms.TypedChoiceField(
        choices=sourceChoices, widget=forms.RadioSelect, coerce=int
        )
    class Meta:
        model = ConnieRun
        exclude = ('boundingBox',)


# The following code to implement multiply-nested formsets is taken
# from http://yergler.net/blog/2009/09/27/nested-formsets-with-django/
# Recall; we need this because for each ConnieRun form we may have a
# number of life phases, each of which can have a number of levels
# associated.

# Make sure we don't display the "blank" option for vertical level:
class LarvalPhaseLevelForm(forms.ModelForm):
    verticalLevel = forms.ModelChoiceField(VelocityFieldsDepth.objects.all(), label='Depth (m)', empty_label=None)
    class Meta:
        model = LarvalPhaseLevel

LarvalLevelFormset = inlineformset_factory(LarvalPhase, LarvalPhaseLevel,
                                           form=LarvalPhaseLevelForm,
                                           extra=1, can_delete=False)

class BaseLarvalPhaseFormset(BaseInlineFormSet):
    def add_fields(self, form, index):
        # allow the super class to create the fields as usual
        super(BaseLarvalPhaseFormset, self).add_fields(form, index)
        
        # created the nested formset
        try:
            instance = self.get_queryset()[index]
            pk_value = instance.pk
        except IndexError:
            instance=None
            # NOTE: this is guaranteed to be signed 32bit on python
            # 2.6; other version differ, see docs.
            pk_value = zlib.crc32(form.prefix)

        # store the formset in the .nested property
        # (note, self.data={} by default which throws an error in django 1.3; see http://docs.djangoproject.com/en/dev/releases/1.3/#formset-updates)
        form.nested = [
            LarvalLevelFormset(data=self.data or None,
                               instance = instance,
                               prefix = 'LEVEL_%s' % pk_value)]

    def is_valid(self):
        result = super(BaseLarvalPhaseFormset, self).is_valid()
        
        for form in self.forms:
            if hasattr(form, 'nested'):
                for n in form.nested:
                    # make sure each nested formset is valid as well
                    self.errors.append(n.errors)
                    result = result and n.is_valid()
                    
        return result

    def save_new(self, form, commit=True):
        """Saves and returns a new model instance for the given form."""
        
        instance = super(BaseLarvalPhaseFormset, self).save_new(form, commit=commit)
        
        # update the form's instance reference
        form.instance = instance
        
        # update the instance reference on nested forms
        for nested in form.nested:
            nested.instance = instance
            
            # iterate over the cleaned_data of the nested formset and
            # update the foreignkey reference
            for cd in nested.cleaned_data:
                cd[nested.fk.name] = instance
                
        return instance

    def should_delete(self, form):
        """Convenience method for determining if the form's object will
        be deleted; cribbed from BaseModelFormSet.save_existing_objects."""
        
        if self.can_delete:
            raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
            should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
            return should_delete
        
        return False
    
    def save_all(self, commit=True):
        """Save all formsets and along with their nested formsets."""
        
        # Save without committing (so self.saved_forms is populated)
        # -- We need self.saved_forms so we can go back and access
        #    the nested formsets
        objects = self.save(commit=False)
        
        # Save each instance if commit=True
        if commit:
            for o in objects:
                o.save()
                
        # save many to many fields if needed
        if not commit:
            self.save_m2m()
            
        # save the nested formsets
        for form in set(self.initial_forms + self.saved_forms):
            if self.should_delete(form): continue
            
            for nested in form.nested:
                nested.save(commit=commit)

LarvalPhaseFormset = inlineformset_factory(ConnieRun, LarvalPhase,
                                           formset=BaseLarvalPhaseFormset,
                                           extra=1, can_delete=False)
